﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Threading;
using System.Windows;
using System.Windows.Forms;
using System.Windows.Media;
using System.Windows.Threading;
using MessageBox = System.Windows.MessageBox;
using OpenFileDialog = Microsoft.Win32.OpenFileDialog;

namespace IISAutoDeployment
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow
    {
        #region Constructors
        public MainWindow()
        {
            InitializeComponent();
            //Startup.StartApp();
            Worker.Sites.ForEach(x =>
                {
                    if(!x.Name.Contains("Maint"))
                        TargetSitesComboBox.Items.Add(x.Name);
                });
            MaintPageComboBox.Items.Add(Worker.Sites.FirstOrDefault(x => x.Name.Contains("Maint")));
            MaintPageComboBox.Items.Add("Staging/Internal/No Maintenance Page Needed");

            _backgroundWorker.WorkerReportsProgress = true;
            _backgroundWorker.WorkerSupportsCancellation = false;
            _backgroundWorker.DoWork += bw_DoWork;
            _backgroundWorker.ProgressChanged += bw_ProgressChanged;
            _backgroundWorker.RunWorkerCompleted += bw_RunWorkerCompleted;
        }

        public void Window_Closed(object sender, RoutedEventArgs e)
        {
            _nodeInput.Close();
            Close();
            System.Windows.Application.Current.Shutdown();
        }
        #endregion

        #region Event Handlers
        private void BackupSelectFileButton_OnClick(object sender, RoutedEventArgs e)
        {
            var oFd = new FolderBrowserDialog();
            if (oFd.ShowDialog() == System.Windows.Forms.DialogResult.OK) 
            {
                BackupFileNameTextBlock.Text = oFd.SelectedPath;
            }
        }

        private void MaintSelectFileButton_OnClick(object sender, RoutedEventArgs e)
        {
            var oFd = new OpenFileDialog();
            oFd.ShowDialog();
            DeployPackageFileNameTextBlock.Text = oFd.FileName;
        }

        private void IsLoadBalancedSite_OnClick(object sender, RoutedEventArgs e)
        {
            if (_hasClicked && IsLoadBalancedSite.IsChecked.GetValueOrDefault())
            {
                IsLoadBalancedSite.IsChecked = false;
                _iisSecondaryNode = null;
                _hasClicked = false;
                return;
            }
            if (!_nodeInput.IsEnabled && _hasClicked)
            {
                _nodeInput.CanceledOutside();
                IsLoadBalancedSite.IsChecked = false;
                _iisSecondaryNode = null;
                _hasClicked = false;
                return;
            }
            
            _nodeInput.IsEnabled = true;
            _nodeInput.Visibility = Visibility.Visible;
            _nodeInput.ShowDialog();
        
            if (_nodeInput.Canceled)
                IsLoadBalancedSite.IsChecked = false;
            else
            {
                _iisSecondaryNode = _nodeInput.SecondaryNodeName;
                _altRemoteBackupLocation = _nodeInput.AlternativeBackupLocation;
                _hasClicked = true;
            }
        }

        private void DeployButton_OnClick(object sender, RoutedEventArgs e)
        {
            // Disclaimer
            if (MessageBox.Show(
                "DISCLAIMER: You confirm that a database backup has been taken for the selected sites targeted in this deployment",
                "Mandatory Check", MessageBoxButton.YesNo, MessageBoxImage.Warning, MessageBoxResult.Yes) ==
                MessageBoxResult.No)
            {
                MessageBox.Show("Cannot proceed unless backup has been taken and confirmed", "Help", MessageBoxButton.OK,
                                MessageBoxImage.Information);
                return;
            }
            
            // Need to validate entries first
            var site = TargetSitesComboBox.Text;
            var maintPage = MaintPageComboBox.Text;
            var deploymentPackage = DeployPackageFileNameTextBlock.Text;
            var backupFolder = BackupFileNameTextBlock.Text;
            var isInternal = IsInternalSite.IsChecked.GetValueOrDefault();
            var isLoadBalancedSite = IsLoadBalancedSite.IsChecked.GetValueOrDefault();
            var runDiff = RunDiff.IsChecked.GetValueOrDefault();
            var runSanityCheck = RunSanityCheck.IsChecked.GetValueOrDefault();
            var copyDependencies = CopyDependencies.IsChecked.GetValueOrDefault();

            if (String.IsNullOrEmpty(site) || String.IsNullOrEmpty(maintPage) || String.IsNullOrEmpty(deploymentPackage) || String.IsNullOrEmpty(backupFolder))
            {
                MessageBox.Show("Please make sure all required items are selected");
                return;
            }
            if (!runDiff)
            {
                if (MessageBox.Show(
                "DISCLAIMER: By skipping the web.config analyzer you confirm that web.config for the selected site is correct",
                "Mandatory Check", MessageBoxButton.OKCancel, MessageBoxImage.Warning, MessageBoxResult.Cancel) == MessageBoxResult.Cancel)
                    return;
            }
            if (_hasRan)
            {
                DeployProgressBar.Value = 0;
                DeployProgressBar.Background = _saveBackground;
                _hasRan = false;
            }
            if (_backgroundWorker.IsBusy)
                return;
            _backgroundWorker.RunWorkerAsync(new WorkerInfo((new Dictionary<String, String>
            {
                {"site", site},
                {"maintPage", maintPage},
                {"deploymentPackage", deploymentPackage},
                {"backupFolder", backupFolder},
                {"isInternal", isInternal.ToString()},
                {"isLoadBalanced", isLoadBalancedSite.ToString()},
                {"secondaryNode", _iisSecondaryNode},
                {"altRemoteBackupLocation", _altRemoteBackupLocation},
                {"runDiff", runDiff.ToString()},
                {"runSanityCheck", runSanityCheck.ToString()},
                {"copyDependencies", copyDependencies.ToString()}
            }
            ),Dispatcher));
        }
        #endregion

        #region Threading
        private static void bw_DoWork(object sender, DoWorkEventArgs e)
        {
            var worker = new Worker();
            var skipRemote = true;
            var backgroundWorker = sender as BackgroundWorker;
            if (backgroundWorker == null)
                return;

            var workerInfo = e.Argument as WorkerInfo;
            if (workerInfo == null) return;
            var dictionary = workerInfo.Args;
            if (dictionary == null) return;
            var site = dictionary["site"];
            var maintPage = dictionary["maintPage"];
            var deploymentPackage = dictionary["deploymentPackage"];
            var backupFolder = dictionary["backupFolder"];
            var isInternal = dictionary["isInternal"];
            var isLoadBalanced = dictionary["isLoadBalanced"];
            var secondaryNode = dictionary["secondaryNode"];
            var altRemoteBackupLocation = dictionary["altRemoteBackupLocation"];
            var runDiff = dictionary["runDiff"];
            var runSanityCheck = dictionary["runSanityCheck"];
            var copyDependencies = dictionary["copyDependencies"];

            if (isLoadBalanced.Contains("True"))
            {
                worker.RemoteWorker(secondaryNode);
                skipRemote = false;
            }
            
            // Sleeps below are for show only: app is too fast, slow it just a tad to display results
            backgroundWorker.ReportProgress(5);
            Thread.Sleep(1000);
            worker.SiteCommand(site, Worker.AllCommands.IisCommands.Stop);
            worker.SiteCommand(site, !skipRemote ? Worker.AllCommands.IisCommands.Stop : Worker.AllCommands.IisCommands.Skip, remoteNode:secondaryNode);
            
            if (!maintPage.Contains("Staging/Internal/No Maintenance Page Needed"))
            {
                backgroundWorker.ReportProgress(10);
                Thread.Sleep(1000);
                worker.SiteCommand(maintPage, Worker.AllCommands.IisCommands.Start);
                worker.SiteCommand(maintPage, !skipRemote ? Worker.AllCommands.IisCommands.Start : Worker.AllCommands.IisCommands.Skip, remoteNode:secondaryNode);
            }
            
            backgroundWorker.ReportProgress(19);
            worker.SiteCommand(site, Worker.AllCommands.IisCommands.Backup, backupFolder);
            worker.SiteCommand(site, !skipRemote ? Worker.AllCommands.IisCommands.Backup : Worker.AllCommands.IisCommands.Skip, 
                String.IsNullOrEmpty(altRemoteBackupLocation) ? backupFolder : altRemoteBackupLocation, secondaryNode);

            backgroundWorker.ReportProgress(50);
            if(!worker.Unzip(deploymentPackage, runSanityCheck))
                throw new Exception("Deployment package unzip Failed");

            if (runDiff.Contains("True"))
            {
                backgroundWorker.ReportProgress(55);
                Thread.Sleep(1000);
                var textDiff = worker.TextDiff(backupFolder);
                if (textDiff == null)
                    throw new Exception("Text Diff Failed");
                if (textDiff.FirstTimeDeploy)
                    MessageBox.Show("No diff possible since this is a first-time deployment", "Skipping Diff",
                                    MessageBoxButton.OK, MessageBoxImage.Information);
                else
                {
                    workerInfo.Dispatcher.Invoke(() =>
                        {
                            var dlg = new DiffResultsWindow(textDiff.FileA, textDiff.FileB, textDiff.ArrayList,
                                                            textDiff.Time);
                            dlg.ShowDialog();
                        });
                    if (MessageBox.Show(
                        "DISCLAIMER: You confirm that the web.config is correct for the selected sites targeted in this deployment",
                        "Mandatory Check",
                        MessageBoxButton.YesNo, MessageBoxImage.Warning, MessageBoxResult.Yes) == MessageBoxResult.No)
                        throw new Exception("Cancel");
                }
            }
            backgroundWorker.ReportProgress(60);
            worker.SiteCommand(site, Worker.AllCommands.IisCommands.Deploy);
            worker.SiteCommand(site, !skipRemote ? Worker.AllCommands.IisCommands.Deploy : Worker.AllCommands.IisCommands.Skip, remoteNode: secondaryNode);

            if (copyDependencies.Contains("True"))
            {
                backgroundWorker.ReportProgress(75);
                Thread.Sleep(1000);
                if(!worker.SiteCommand(site, Worker.AllCommands.IisCommands.Dependencies))
                    MessageBox.Show("Could not copy BLB files. Check source directory and copy files manually", "Skipping Dependency Copy",
                                    MessageBoxButton.OK, MessageBoxImage.Information);
                if (!worker.SiteCommand(site, !skipRemote ? Worker.AllCommands.IisCommands.Dependencies : Worker.AllCommands.IisCommands.Skip, remoteNode: secondaryNode))
                    MessageBox.Show("Could not copy BLB files on secondary Node. Check source directory and copy files manually", "Skipping Dependency Copy",
                                    MessageBoxButton.OK, MessageBoxImage.Information);
            }

            backgroundWorker.ReportProgress(85);
            Thread.Sleep(1000);
            if (!maintPage.Contains("Staging/Internal/No Maintenance Page Needed"))
            {
                worker.SiteCommand(maintPage, Worker.AllCommands.IisCommands.Stop);
                worker.SiteCommand(maintPage, !skipRemote ? Worker.AllCommands.IisCommands.Stop : Worker.AllCommands.IisCommands.Skip, remoteNode: secondaryNode);
                backgroundWorker.ReportProgress(90);
            }
            worker.SiteCommand(site, Worker.AllCommands.IisCommands.Start);
            worker.SiteCommand(site, !skipRemote ? Worker.AllCommands.IisCommands.Start : Worker.AllCommands.IisCommands.Skip, remoteNode: secondaryNode);
            backgroundWorker.ReportProgress(95);
            if (isInternal.Contains("False"))
                worker.SiteCommand(site, Worker.AllCommands.IisCommands.Browse);
        }

        private void bw_ProgressChanged(object sender, ProgressChangedEventArgs e)
        {
            switch (e.ProgressPercentage)
            {
                case 0:
                    DeploymentStatusTextBlock.Text = "Deployment Stopped";
                    break;
                case 5:
                    DeploymentStatusTextBlock.Text = "Stop the site";
                    break;
                case 10:
                    DeploymentStatusTextBlock.Text = "Start Maintenance Page";
                    break;
                case 19:
                    DeploymentStatusTextBlock.Text = "Backing up site";
                    break;
                case 50:
                    DeploymentStatusTextBlock.Text = "Unzip deployment package";
                    break;
                case 55:
                    DeploymentStatusTextBlock.Text = "Run Diff on web.configs";
                    break;
                case 60:
                    DeploymentStatusTextBlock.Text = "Deploy new site code";
                    break;
                case 75:
                    DeploymentStatusTextBlock.Text = "Copying Dependencies";
                    break;
                case 85:
                    DeploymentStatusTextBlock.Text = "Bring up site and recycle AppPool";
                    break;
            }
            DeployProgressBar.Value = e.ProgressPercentage;
        }

        private void bw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            _saveBackground = DeployProgressBar.Background;
            if (e.Error != null)
            {
                DeployProgressBar.Background = new SolidColorBrush(Colors.Red);
                if (e.Error.Message.Contains("Cancel"))
                    DeploymentStatusTextBlock.Text = "Canceled";
                else
                {
                    DeploymentStatusTextBlock.Text = e.Error.Message;
                    MessageBox.Show("Error: " + e.Error.Message);
                }
            }
            else
            {
                DeployProgressBar.Value = 100;
                DeploymentStatusTextBlock.Text = "Completed";
            }
            _hasRan = true;
        }
        #endregion

        #region Properties
        private readonly BackgroundWorker _backgroundWorker = new BackgroundWorker();
        private Brush _saveBackground;
        private bool _hasRan;
        private bool _hasClicked;
        private static readonly Worker Worker = new Worker();
        private readonly SecondaryNodeInput _nodeInput = new SecondaryNodeInput();
        private string _iisSecondaryNode;
        private string _altRemoteBackupLocation;
        #endregion
    }
    #region Threading Arguments Class
    /// <summary>
    /// Abstraction for arguments required by background worker tasks
    /// </summary>
    public class WorkerInfo
    {
        public Dictionary<string, string> Args { get; set; }
        public Dispatcher Dispatcher { get; set; }

        public WorkerInfo(Dictionary<string, string> args, Dispatcher dispatcher)
        {
            Args = args;
            Dispatcher = dispatcher;
        }
    }
    #endregion
}
